/**
 * (c) planet-ix ltd 2005
 */
package com.ixcode.framework.parameter.model;

import com.ixcode.framework.experiment.model.xml.ParameterReferenceNode;
import com.ixcode.framework.javabean.JavaBeanModelAdapter;
import com.ixcode.framework.javabean.format.IJavaBeanValueFormat;
import com.ixcode.framework.javabean.format.JavaBeanFormatter;
import com.ixcode.framework.javabean.format.TypeSafeEnumFormat;
import com.ixcode.framework.math.geometry.*;
import com.ixcode.framework.math.scale.ScaledDistance;
import com.ixcode.framework.math.scale.ScaledDistanceFormat;
import com.ixcode.framework.xml.XMLAttributes;
import com.ixcode.framework.xml.XMLWriter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

/**
 * Description : ${CLASS_DESCRIPTION}
 */
public class ParameterMapXML extends XMLWriter {
    public static void exportParams(File outputFile, String description, ParameterMap params) throws IOException {
        if (outputFile.exists()) {
            throw new IOException("File already Exists: " + outputFile.getAbsolutePath());
        }

        PrintWriter out = null;
        try {
            outputFile.createNewFile();
            out = new PrintWriter(new FileOutputStream(outputFile));

            out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            openComment(0, out, "File generated by bugsim", true);
            openComment(0, out, description, true);

            String fileId = outputFile.getName().substring(0, outputFile.getName().lastIndexOf("."));
            XMLAttributes attr = createParameterMapAttributes(fileId);
            openTag(0, out, attr, "parameterMap");
            exportCategories(1, out, params.getCategories());
            closeTag(0, out, "parameterMap");
        } finally {

            if (out != null) {
                out.close();
            }
        }

    }


    public static XMLAttributes createParameterMapAttributes(String fileId) {
        XMLAttributes attr = new XMLAttributes();

        attr.add("fileId", fileId);
        return attr;
    }

    public static void exportCategories(int indent, PrintWriter out, List categories) {
        for (Iterator itr = categories.iterator(); itr.hasNext();) {
            Category category = (Category)itr.next();
            exportCategory(indent+1, out, category);
        }
    }

    private static void exportCategory(int indent, PrintWriter out, Category category) {
        XMLAttributes attr = createCategoryAttributes(category);
        openTag(indent, out, attr, "category");

        exportParameters(indent + 1, out, category.getParameters());

        if (category.getSubCategories().size() > 0) {
            openTag(indent + 1, out, EMPTY_ATTRIBUTES, "subCategories");
            exportCategories(indent + 2, out, category.getSubCategories());
            closeTag(indent + 1, out, "subCategories");            
        }
        closeTag(indent, out, "category");

    }

    private static XMLAttributes createCategoryAttributes(Category category) {
        XMLAttributes attr = new XMLAttributes();
        attr.add("name", category.getName());
        return attr;
    }

    private static void exportParameters(int indent, PrintWriter out, List parameters) {
        exportParameters(indent, out, parameters, false);
    }

    private static void exportParameters(int indent, PrintWriter out, List parameters, boolean fqName) {


        for (Iterator itr = parameters.iterator(); itr.hasNext();) {

            Parameter parameter = (Parameter)itr.next();


            if (parameter instanceof StrategyDefinitionParameter) {
                exportStrategyParameter(indent, out, (StrategyDefinitionParameter)parameter, fqName);

            } else if (parameter instanceof DerivedParameter) {

                DerivedParameter derived = (DerivedParameter)parameter;
                XMLAttributes attr = createSimpleParameterAttributes(parameter, fqName);
                openTag(indent, out, attr, "derivedParameter");

                openTag(indent + 1, out, createDerivedCalculationAttributes(derived.getCalculation()), "calculation", true, true);
                openTag(indent + 1, out, EMPTY_ATTRIBUTES, "sourceParameters");
                exportSourceParameters(indent + 2, out, derived.getSourceParameters().getParameterReferences());
                closeTag(indent + 1, out, "sourceParameters");

                closeTag(indent, out, "derivedParameter");

            } else {
                exportSimpleParameter(parameter, indent, out, itr, fqName);


            }


        }
    }

    private static void exportSourceParameters(int indent, PrintWriter out, List sourceParameters) {
        for (Iterator itr = sourceParameters.iterator(); itr.hasNext();) {
            Parameter parameter = (Parameter)itr.next();
            exportParameterReference(parameter, indent, out);
        }
    }

    public static void exportParameterReference(Parameter parameter, int indent, PrintWriter out) {
        XMLAttributes attr = new XMLAttributes();
        String ref = (parameter instanceof ParameterReference) ? ((ParameterReference)parameter).getReference() : parameter.getFullyQualifiedName();
        attr.add(ParameterReferenceNode.A_NAME, parameter.getName());
        attr.add(ParameterReferenceNode.A_FQ_NAME, ref);
        openTag(indent, out, attr, "parameterReference", true, true);
    }


    private static XMLAttributes createDerivedCalculationAttributes(IDerivedParameterCalculation calculation) {
        XMLAttributes attr = new XMLAttributes();
        attr.add("className", calculation.getClass().getName());
        return attr;
    }

    private static void exportSimpleParameter(Parameter parameter, int indent, PrintWriter out, Iterator itr, boolean fqName) {
        XMLAttributes attr = createSimpleParameterAttributes(parameter, fqName);
        boolean close = (parameter.containsStrategy() || (parameter.getValue() instanceof Collection)) ? false : true;
        openTag(indent, out, attr, "parameter", close, true);

        if (parameter.containsStrategy()) {
            StrategyDefinitionParameter strategyP = (StrategyDefinitionParameter)parameter.getValue();
            exportStrategyParameter(indent + 1, out, strategyP, fqName);

        } else if (parameter.getValue() instanceof Collection) {
            Collection col = (Collection)parameter.getValue();
            if (col.size() > 0 && Parameter.class.isAssignableFrom(col.iterator().next().getClass())) {
                openTag(indent+1, out, EMPTY_ATTRIBUTES, "parameterList");
                exportParameters(indent + 2, out, (List)col);
                closeTag(indent+1, out, "parameterList");
            } else {
                XMLAttributes colattr = createCollectionAttributes(col);
                if (col.size() > 0) {
                    colattr.add("firstItemType", col.iterator().next().getClass().getName());
                }

                openTag(indent + 1, out, colattr, "valueCollection");
                for (Iterator itrValue = col.iterator(); itrValue.hasNext();) {
                    Object o = itrValue.next();
                    XMLAttributes itemattr = new XMLAttributes();
                    itemattr.add("value", o);
                    itemattr.add("type", o.getClass().getName());

                    openTag(indent + 2, out, itemattr, "item", true, true);
                }
                closeTag(indent + 1, out, "valueCollection");
            }
        }
        if (!close) {
            closeTag(indent, out, "parameter");
        }

    }

    private static XMLAttributes createCollectionAttributes(Collection col) {
        XMLAttributes attr = new XMLAttributes();
        attr.add("type", col.getClass().getName());
        attr.add("size", new Integer(col.size()));
        return attr;
    }


    private static XMLAttributes createSimpleParameterAttributes(Parameter parameter, boolean fqName) {
        XMLAttributes attr = new XMLAttributes();

        if (fqName) {
            attr.add("name", parameter.getFullyQualifiedName());
        } else {
            attr.add("name", parameter.getName());
        }

        if (parameter instanceof DerivedParameter) {
            attr.add("derived", Boolean.TRUE);
        }

        if (parameter.containsStrategy()) {
            attr.add("containsStrategy", Boolean.TRUE);
        } else if (!(parameter.getValue() instanceof Collection)) {



            if (parameter.getValue() != null) {
                JavaBeanFormatter formatter = MODEL_ADAPTER.getFormatter();
                IJavaBeanValueFormat format = formatter.getFormat(Locale.UK,  parameter.getValue().getClass());
                attr.add("value", format.format(parameter.getValue()));
                attr.add("type", parameter.getValue().getClass().getName());
            } else {
                attr.add("value", null);
            }
        }

        if (parameter.hasComment()) {
            attr.add("comment", parameter.getComment());
        }

        return attr;
    }


    public static void exportStrategyParameter(int indent, PrintWriter out, StrategyDefinitionParameter strategy, boolean fqName) {
        XMLAttributes attr = createStrategyParameterAttributes(strategy, fqName);
        openTag(indent, out, attr, "strategy");
        openTag(indent + 1, out, EMPTY_ATTRIBUTES, "implementingClass", false, false);
        out.print(strategy.getImplementingClassName());
        closeTag(0, out, "implementingClass");

        openTag(indent + 1, out, EMPTY_ATTRIBUTES, "strategyParameters");
        exportParameters(indent + 2, out, strategy.getParameters());
        closeTag(indent + 1, out, "strategyParameters");
        closeTag(indent, out, "strategy");
    }

    private static XMLAttributes createStrategyParameterAttributes(StrategyDefinitionParameter strategy, boolean fqName) {
        XMLAttributes attr = new XMLAttributes();
        if (fqName) {
            attr.add("name", strategy.getFullyQualifiedName());
        } else {
            attr.add("name", strategy.getName());
        }


        return attr;
    }


    public static  void initModelAdapter(JavaBeanModelAdapter modelAdapter) {
        initFormatter(modelAdapter.getFormatter());
    }

    public static  void initFormatter(JavaBeanFormatter formatter) {
        formatter.registerFormat(Locale.UK, CartesianBounds.class, new CartesianBoundsFormat());
        formatter.registerFormat(Locale.UK, CartesianDimensions.class, new CartesianDimensionsFormat());
        formatter.registerFormat(Locale.UK, RectangularCoordinate.class, new RectangularCoordinateFormat());
        formatter.registerFormat(Locale.UK, ScaledDistance.class, new ScaledDistanceFormat());
        formatter.registerFormat(Locale.UK, ShapeLocationType.class, new TypeSafeEnumFormat(ShapeLocationType.class));
        
    }

    


    private static JavaBeanModelAdapter MODEL_ADAPTER = new JavaBeanModelAdapter();
    static {
        initModelAdapter(MODEL_ADAPTER);
    }
}


